# -*- coding: utf-8 -*-

import os
import sys

from utils.pyhelper import *

MAIN_NAME = 'maru_main'
STRUCT_NAME = 'OP_FILE'
FORMAT_DICT_NAME = 'MARU_FORMATS'


def hla_assemble_to_struct(file_path: str):
    # Import the Python source code.
    file_module = py_load_from_path(file_path)
    # Call the main function inside the module.
    if not hasattr(file_module, MAIN_NAME):
        raise Exception('Failed to find entry {}() in file {}'.format(MAIN_NAME, file_path))
    main_method = getattr(file_module, MAIN_NAME)
    main_method()
    # Now we should have all the method in the base of the MTL.
    if not hasattr(file_module, STRUCT_NAME):
        raise Exception('Failed to find generated structure in file {}.'.format(file_path))
    op_file = getattr(file_module, STRUCT_NAME)
    # Give back the op file.
    return op_file


def hla_find_format(format: str, output_path: str, arch_path: str):
    # Extract the format.
    format_py_path = os.path.join(arch_path, 'format.py')
    if not os.path.isfile(format_py_path):
        raise Exception('Failed to find format.py at {}.'.format(format_py_path))
    # Load the format module.
    format_module = py_load_from_path(format_py_path)
    if not hasattr(format_module, FORMAT_DICT_NAME):
        raise Exception('Failed to find {} in format.py.'.format(FORMAT_DICT_NAME))
    # Extract the format dictionary.
    arch_format = getattr(format_module, FORMAT_DICT_NAME)
    # Find the format rules in the arch format.
    if format is None:
        # Check whether we have the output format.
        if output_path is None:
            raise Exception('Please specify either the output format or output path.')
        # Based on the suffix of the output file, guess the format of the file.
        _, ext_name = os.path.splitext(output_path)
        if ext_name.startswith('.'):
            ext_name = ext_name[1:]
        if len(ext_name) == 0:
            raise Exception('Please specify the output format file.')
        # Search the extension in the dictionary.
        for format_name in arch_format:
            method, suffix = arch_format[format_name]
            if suffix == ext_name:
                # Use the method to dump the format.
                return method
        # Or else, failed to find suffix.
        raise Exception('Failed to find generate method of suffix {}'.format(ext_name))
    # Format is not none, directly search in dictionary.
    if format not in arch_format:
        raise Exception('Format {} is not supported by the architecture.'.format(format))
    # Extract the method
    method, _ = arch_format[format]
    return method


def hla_dump(op_file, format: str, output_path: str, arch_path: str):
    # Search the dump method.
    dump_method = hla_find_format(format, output_path, arch_path)
    # Dump to the target file.
    dump_method(op_file, output_path)


def hla_assemble(file_path: str, mtl_path: str, format: str, output_path: str, arch_path: str):
    # Add the template library to search path.
    sys.path.insert(1, mtl_path)
    # Append arch directory name to the end of arch path.
    arch_path = os.path.join(os.path.abspath(arch_path), 'arch')
    # Get the absolute path of the file.
    file_path = os.path.abspath(file_path)
    # Compile the file into output structure.
    binary_struct = hla_assemble_to_struct(file_path)
    # Now we should output the file based on the format.
    hla_dump(binary_struct, format, output_path, arch_path)